/***
* ==++==
*
* Copyright (c) Microsoft Corporation.  All rights reserved.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* http_msg.h
*
* HTTP Library: Request and reply message definitions.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "pplxtasks.h"
#include "json.h"
#include "uri.h"
#include "log.h"
#include "xxpublic.h"
#include "xxwin32.h"
#include "asyncrt_utils.h"

/// <summary>
/// Declaration to avoid making a dependency on IISHost.
/// </summary>
namespace iis_host
{
    class http_iis_receiver;
}

namespace http
{
namespace client
{
    class http_client;
}
namespace listener
{
    class http_listener;
}

//
// Predefined method strings for the standard HTTP methods mentioned in the
// HTTP 1.1. specification.
//
typedef std::wstring method;
class methods
{
public:
    const static method GET;
    const static method POST;
    const static method PUT;
    const static method DEL;
    const static method HEAD;
    const static method OPTIONS;
    const static method TRCE;
    const static method CONNECT;
};
__declspec(selectany) const method methods::GET = L"GET";
__declspec(selectany) const method methods::POST = L"POST";
__declspec(selectany) const method methods::PUT = L"PUT";
__declspec(selectany) const method methods::DEL = L"DELETE";
__declspec(selectany) const method methods::HEAD = L"HEAD";
__declspec(selectany) const method methods::OPTIONS = L"OPTIONS";
__declspec(selectany) const method methods::TRCE = L"TRACE";
__declspec(selectany) const method methods::CONNECT = L"CONNECT";

typedef unsigned short status_code;
/// <summary>
/// Predefined values for all of the standard HTTP 1.1 response status codes.
/// </summary>
class status_codes
{
public:
    const static status_code Continue = 100;
    const static status_code SwitchingProtocols = 101;
    const static status_code OK = 200;
    const static status_code Created = 201;
    const static status_code Accepted = 202;
    const static status_code NonAuthInfo = 203;
    const static status_code NoContent = 204;
    const static status_code ResetContent = 205;
    const static status_code PartialContent = 206;
    const static status_code MultipleChoices = 300;
    const static status_code MovedPermanently = 301;
    const static status_code Found = 302;
    const static status_code SeeOther = 303;
    const static status_code NotModified = 304;
    const static status_code UseProxy = 305;
    const static status_code TemporaryRedirect = 307;
    const static status_code BadRequest = 400;
    const static status_code Unauthorized = 401;
    const static status_code PaymentRequired = 402;
    const static status_code Forbidden = 403;
    const static status_code NotFound = 404;
    const static status_code MethodNotAllowed = 405;
    const static status_code NotAcceptable = 406;
    const static status_code ProxyAuthRequired = 407;
    const static status_code RequestTimeout = 408;
    const static status_code Conflict = 409;
    const static status_code Gone = 410;
    const static status_code LengthRequired = 411;
    const static status_code PreconditionFailed = 412;
    const static status_code RequestEntityTooLarge = 413;
    const static status_code RequestUriTooLarge = 414;
    const static status_code UnsupportedMediaType = 415;
    const static status_code RangeNotSatisfiable = 416;
    const static status_code ExpectationFailed = 417;
    const static status_code InternalError = 500;
    const static status_code NotImplemented = 501;
    const static status_code BadGateway = 502;
    const static status_code ServiceUnavailable = 503;
    const static status_code GatewayTimeout = 504;
    const static status_code HttpVersionNotSupported = 505;
};

typedef std::wstring reason_phrase;
/// <summary>
/// Predefined values for all of the standard HTTP 1.1 response reason phrases.
/// </summary>
class reason_phrases
{
public:
    const static reason_phrase Continue;
    const static reason_phrase SwitchingProtocols;
    const static reason_phrase OK;
    const static reason_phrase Created;
    const static reason_phrase Accepted;
    const static reason_phrase NonAuthInfo;
    const static reason_phrase NoContent;
    const static reason_phrase ResetContent;
    const static reason_phrase PartialContent;
    const static reason_phrase MultipleChoices;
    const static reason_phrase MovedPermanently;
    const static reason_phrase Found;
    const static reason_phrase SeeOther;
    const static reason_phrase NotModified;
    const static reason_phrase UseProxy;
    const static reason_phrase TemporaryRedirect;
    const static reason_phrase BadRequest;
    const static reason_phrase Unauthorized;
    const static reason_phrase PaymentRequired;
    const static reason_phrase Forbidden;
    const static reason_phrase NotFound;
    const static reason_phrase MethodNotAllowed;
    const static reason_phrase NotAcceptable;
    const static reason_phrase ProxyAuthRequired;
    const static reason_phrase RequestTimeout;
    const static reason_phrase Conflict;
    const static reason_phrase Gone;
    const static reason_phrase LengthRequired;
    const static reason_phrase PreconditionFailed;
    const static reason_phrase RequestEntityTooLarge;
    const static reason_phrase RequestUriTooLarge;
    const static reason_phrase UnsupportedMediaType;
    const static reason_phrase RangeNotSatisfiable;
    const static reason_phrase ExpectationFailed;
    const static reason_phrase InternalError;
    const static reason_phrase NotImplemented;
    const static reason_phrase BadGateway;
    const static reason_phrase ServiceUnavailable;
    const static reason_phrase GatewayTimeout;
    const static reason_phrase HttpVersionNotSupported;

    // Constructor to initialize status codes to reason_phrases map.
    reason_phrases();

    // Map of status codes to reason phrases.
    std::map<status_code, reason_phrase> _M_statusCodeToReasonPhrases;
};
__declspec(selectany) const reason_phrase reason_phrases::Continue = L"Continue";
__declspec(selectany) const reason_phrase reason_phrases::SwitchingProtocols = L"Switching Protocols";
__declspec(selectany) const reason_phrase reason_phrases::OK = L"OK";
__declspec(selectany) const reason_phrase reason_phrases::Created = L"Created";
__declspec(selectany) const reason_phrase reason_phrases::Accepted = L"Accepted";
__declspec(selectany) const reason_phrase reason_phrases::NonAuthInfo = L"Non-Authoritative Information";
__declspec(selectany) const reason_phrase reason_phrases::NoContent = L"No Content";
__declspec(selectany) const reason_phrase reason_phrases::ResetContent = L"Reset Content";
__declspec(selectany) const reason_phrase reason_phrases::PartialContent = L"Partial Content";
__declspec(selectany) const reason_phrase reason_phrases::MultipleChoices = L"Multiple Choices";
__declspec(selectany) const reason_phrase reason_phrases::MovedPermanently = L"Moved Permanently";
__declspec(selectany) const reason_phrase reason_phrases::Found = L"Found";
__declspec(selectany) const reason_phrase reason_phrases::SeeOther = L"See Other";
__declspec(selectany) const reason_phrase reason_phrases::NotModified = L"Not Modified";
__declspec(selectany) const reason_phrase reason_phrases::UseProxy = L"Use Proxy";
__declspec(selectany) const reason_phrase reason_phrases::TemporaryRedirect = L"Temporary Redirect";
__declspec(selectany) const reason_phrase reason_phrases::BadRequest = L"Bad Request";
__declspec(selectany) const reason_phrase reason_phrases::Unauthorized = L"Unauthorized";
__declspec(selectany) const reason_phrase reason_phrases::PaymentRequired = L"Payment Required";
__declspec(selectany) const reason_phrase reason_phrases::Forbidden = L"Forbidden";
__declspec(selectany) const reason_phrase reason_phrases::NotFound = L"Not Found";
__declspec(selectany) const reason_phrase reason_phrases::MethodNotAllowed = L"Method Not Allowed";
__declspec(selectany) const reason_phrase reason_phrases::NotAcceptable = L"Not Acceptable";
__declspec(selectany) const reason_phrase reason_phrases::ProxyAuthRequired = L"Proxy Authentication Required";
__declspec(selectany) const reason_phrase reason_phrases::RequestTimeout = L"Request Time-out";
__declspec(selectany) const reason_phrase reason_phrases::Conflict = L"Conflict";
__declspec(selectany) const reason_phrase reason_phrases::Gone = L"Gone";
__declspec(selectany) const reason_phrase reason_phrases::LengthRequired = L"Length Required";
__declspec(selectany) const reason_phrase reason_phrases::PreconditionFailed = L"Precondition Failed";
__declspec(selectany) const reason_phrase reason_phrases::RequestEntityTooLarge = L"Request Entity Too Large";
__declspec(selectany) const reason_phrase reason_phrases::RequestUriTooLarge = L"Request Uri Too Large";
__declspec(selectany) const reason_phrase reason_phrases::UnsupportedMediaType = L"Unsupported Media Type";
__declspec(selectany) const reason_phrase reason_phrases::RangeNotSatisfiable = L"Requested range not satisfiable";
__declspec(selectany) const reason_phrase reason_phrases::ExpectationFailed = L"Expectation Failed";
__declspec(selectany) const reason_phrase reason_phrases::InternalError = L"Internal Error";
__declspec(selectany) const reason_phrase reason_phrases::NotImplemented = L"Not Implemented";
__declspec(selectany) const reason_phrase reason_phrases::BadGateway = L"Bad Gateway";
__declspec(selectany) const reason_phrase reason_phrases::ServiceUnavailable = L"Service Unavaliable";
__declspec(selectany) const reason_phrase reason_phrases::GatewayTimeout = L"Gateway Time-out";
__declspec(selectany) const reason_phrase reason_phrases::HttpVersionNotSupported = L"HTTP Version not supported";

/// <summary>
/// Class representing HTTP headers, acts like a map.
/// </summary>
class http_headers
{
public:
    // Function object to perform case insensitive comparison of wstrings.
    struct _case_insensitive_cmp
    {
        bool operator()(const std::wstring &str1, const std::wstring &str2) const
        {
            return _wcsicmp(str1.c_str(), str2.c_str()) < 0;
        }
    };

    /// <summary>
    /// STL-style typedefs
    /// </summary>
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::key_type key_type;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::key_compare key_compare;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::allocator_type allocator_type;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::size_type size_type;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::difference_type difference_type;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::pointer pointer;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::const_pointer const_pointer;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::reference reference;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::const_reference const_reference;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::iterator iterator;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::const_iterator const_iterator;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::reverse_iterator reverse_iterator;
    typedef std::map<std::wstring, std::wstring, _case_insensitive_cmp>::const_reverse_iterator const_reverse_iterator;

    /// <summary>
    /// Constructs an empty set of HTTP headers.
    /// </summary>
    http_headers() {}

    /// <summary>
    /// Copy constructor.
    /// </summary>
    http_headers(const http_headers &other) : m_headers(other.m_headers) {}

    /// <summary>
    /// Assignment operator.
    /// </summary>
    http_headers &operator=(const http_headers &other)
    {
        if(this != &other)
        {
            m_headers = other.m_headers;
        }
        return *this;
    }

    /// <summary>
    /// Move constructor.
    /// </summary>
    http_headers(http_headers &&other) : m_headers(std::move(other.m_headers)) {}

    /// <summary>
    /// Move assignment operator.
    /// </summary>
    http_headers &operator=(http_headers &&other)
    {
        if(this != &other)
        {
            m_headers = std::move(other.m_headers);
        }
        return *this;
    }

    /// <summary>
    /// Add a header field using the '<<' operator.
    /// </summary>
    /// <param name="name">The name of the header field.</param>
    /// <param name="value">The value of the header field.</param>
    template<typename _t1>
    void add(const key_type& name, const _t1 &value)
    {
        std::wostringstream ss;
        ss << value;
        m_headers[name] = ss.str();
    }

    /// <summary>
    /// Add a header field.
    /// </summary>
    /// <param name="name">The name of the header field.</param>
    /// <param name="value">The value of the header field.</param>
    void add(const key_type& name, const std::string &value)
    {
        m_headers[name] = utilities::conversions::utf8_to_utf16(value);
    }

    /// <summary>
    /// Removes a header field.
    /// </summary>
    /// <param name="name">The name of the header field.</param>
    void remove(const key_type& name)
    {
        m_headers.erase(name);
    }

    /// <summary>
    /// Checks if there is a header with the given key.
    /// </summary>
    /// <param name="name">The name of the header field.</param>
    /// <returns>True if there is a header with the given name, false otherwise.</returns>
    bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); }

    /// <summary>
    /// Returns the number of header fields.
    /// </summary>
    /// <returns>Number of header fields.</returns>
    size_type size() const { return m_headers.size(); }

    /// <summary>
    /// Tests to see if there are any header fields.
    /// </summary>
    bool empty() const { return m_headers.empty(); }

    /// <summary>
    /// Returns a reference to header field with given name, if there is no header field one is inserted.
    /// </summary>
    std::wstring & operator[](const key_type &name) { return m_headers[name]; }

    /// <summary>
    /// Checks if a header field exists with given name and returns an iterator if found. Otherwise
    /// and iterator to end is returned.
    /// </summary>
    iterator find(const key_type &name) { return m_headers.find(name); }
    const_iterator find(const key_type &name) const { return m_headers.find(name); }

    /// <summary>
    /// Attempts to match a header field with the given name using the '>>' operator.
    /// </summary>
    /// <param name="name">The name of the header field.</param>
    /// <param name="value">The value of the header field.</param>
    /// <returns>True if header field was found and successfully stored in value parameter.</returns>
    template<typename _t1>
    bool match(const key_type &name, _t1 &value) const
    {
        auto iter = m_headers.find(name);
        if (iter != m_headers.end())
        {
            // Check to see if doesn't have a value.
            if(iter->second.empty())
            {
                this->bind(iter->second, value);
                return true;
            }
            return this->bind(iter->second, value);
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// Returns an iterator refering to the first header field.
    /// </summary>
    iterator begin() { return m_headers.begin(); }
    const_iterator begin() const { return m_headers.begin(); }

    /// <summary>
    /// Returns an iterator referring to the past-the-end header field.
    /// </summary>
    iterator end() { return m_headers.end(); }
    const_iterator end() const { return m_headers.end(); }

    /// <summary>
    /// Get a reference to the content type of the message.
    /// </summary>
    /// <returns>The content type of the body.</returns>
    _ASYNCRTIMP const std::wstring &content_type() const;

    /// <summary>
    /// Set the content type of the message.
    /// </summary>
    /// <param name="type">The content type of the body.</param>
    _ASYNCRTIMP void set_content_type(const std::wstring& type);

private:

    /// <summary>
    /// Binds an individual reference to a string value.
    /// </summary>
    template<typename _t>
    bool bind(const key_type &text, _t &ref) const
    {
        std::wistringstream iss(text);
        iss >> ref;
        if (iss.fail() || !iss.eof())
        {
            return false;
        }

        return true;
    }

    // This specialization is need because istringstream '>>' delimits on whitespace.
    template <>
    bool bind(const key_type &text, std::wstring &ref) const 
    { 
        ref = text; 
        return true; 
    }

    // This specialization is need because istringstream '>>' delimits on whitespace.
    template <>
    bool bind(const key_type &text, std::string &ref) const 
    { 
        ref = utilities::conversions::utf16_to_utf8(text); 
        return true; 
    }

    // Headers are stored in a map with case insensitive key.
    std::map<std::wstring, std::wstring, _case_insensitive_cmp> m_headers;
};

namespace details
{

// Generic byte interface to a data buffer.
class data_buffer_interface
{
public:
    virtual ~data_buffer_interface() {}
    virtual const unsigned char * body() const = 0;
    virtual size_t body_size() const = 0;
    virtual void resize(size_t new_size) = 0;
};

// Class for storing a series of bytes as a raw pointer.
// NOTE: this class doesn't actual own the raw memory.
class raw_bytes_buffer
{
public:
    raw_bytes_buffer(unsigned char *data, size_t size) : m_data(data), m_size(size) {}

    // Necessary to use with typed_data_buffer.
    typedef unsigned char value_type;

    size_t size() const { return m_size; }
    bool empty() const { return m_size == 0; }
    unsigned char &operator[](size_t index) const { return m_data[index]; }

    // Resize isn't supported.
    void resize(size_t) { throw std::exception("Resize isn't supported on raw_bytes_buffer"); };

private:
    unsigned char* m_data;
    size_t m_size;
};

// Buffer to hold typed data buffer.
template <typename T>
class typed_data_buffer : public data_buffer_interface
{
public:
    typed_data_buffer() {}
    typed_data_buffer(T &&data) : m_data(std::move(data)) {}

    const unsigned char * body() const
    {
        if(m_data.empty())
        {
            return nullptr;
        }
        else
        {
            return (unsigned char *)&m_data[0];
        }
    }

    size_t body_size() const { return m_data.size() * sizeof(T::value_type); }

    void resize(size_t new_size) { m_data.resize((size_t)(new_size / sizeof(T::value_type))); }

    T & raw_body() { return m_data; }

    T move_body() { return std::move(m_data); }

private:
    typed_data_buffer(const typed_data_buffer &);
    typed_data_buffer &operator=(const typed_data_buffer &);

    T m_data;
};

// Interface to typed_data_buffer which hides the actual implementation type.
class data_buffer : public data_buffer_interface
{
public:
    data_buffer() : m_data(nullptr) {}
    ~data_buffer() { delete_data(); }

    const unsigned char * body() const 
    { 
        if(m_data == nullptr)
        {
            return nullptr;
        }
        else
        {
            return m_data->body(); 
        }
    }

    size_t body_size() const 
    { 
        if(m_data == nullptr)
        {
            return 0;
        }
        else
        {
            return m_data->body_size(); 
        }
    }

    void resize(size_t new_size)
    {
        if(m_data == nullptr)
        {
            throw std::exception("There is no data in the buffer to resize");
        }
        else
        {
            m_data->resize(new_size);
        }
    }

    template <typename T>
    typed_data_buffer<T> &set_buffer()
    {
        delete_data();
        typed_data_buffer<T> *data = new typed_data_buffer<T>();
        m_data = data;
        return *data;
    }

    template <typename T>
    typed_data_buffer<T> &set_buffer(T &&buffer)
    {
        delete_data();
        typed_data_buffer<T> *data = new typed_data_buffer<T>(std::move(buffer));
        m_data = data;
        return *data;
    }

    data_buffer_interface * raw_pointer() { return m_data; }

private:
    void delete_data()
    {
        delete m_data;
        m_data = nullptr;
    }

    data_buffer_interface *m_data;
};

/// <summary>
/// Base class for http messages.
/// This class is to store common functionality so it isn't duplicated on
/// both the request and response side.
/// </summary>
class _http_modifiable_msg_base
{
public:

    _ASYNCRTIMP _http_modifiable_msg_base();

    virtual ~_http_modifiable_msg_base() {}

    http_headers &headers() { return m_headers; }

    const unsigned char * body() const { return m_body.body(); }

    size_t body_size() const { return m_body.body_size(); }

    _ASYNCRTIMP std::string extract_string();

    _ASYNCRTIMP std::wstring extract_wstring();

    _ASYNCRTIMP json::value extract_json();

    _ASYNCRTIMP std::vector<unsigned char> extract_vector();

    _ASYNCRTIMP void set_text(const std::string &body_text, const std::wstring &content_type);

    _ASYNCRTIMP void set_body(std::string &&body_text);

    _ASYNCRTIMP void set_body(std::wstring &&body_text);
    
    _ASYNCRTIMP void set_body(std::vector<unsigned char> &&body_data);
    
    _ASYNCRTIMP void set_body(const unsigned char *body_data, const size_t body_length);

    _ASYNCRTIMP void copy_body(const unsigned char *body_data, const size_t body_length);

    virtual _ASYNCRTIMP std::string to_string() const;

    virtual _ASYNCRTIMP std::wstring to_wstring() const;

    void resize_body(size_t size) { m_body.resize(size); }

    // Access to message body implementation.
    data_buffer & _body() { return m_body; }

    _ASYNCRTIMP void _get_body(unsigned char *&body_data, size_t &body_length);

protected:

    http_headers m_headers;
    data_buffer m_body;
};

/// <summary>
/// Base structure for associating internal server information
/// with an HTTP request/response.
/// </summary>
class _http_server_context
{
public:
    _http_server_context() {}
    virtual ~_http_server_context() {}
private:
};

/// <summary>
/// Internal representation of an HTTP response.
/// </summary>
class _http_response : public http::details::_http_modifiable_msg_base
{
public:
    _http_response() : m_status_code(USHRT_MAX) { }

    _http_response(status_code code) : m_status_code(code) {}

    status_code status_code() const { return m_status_code; } 

    void set_status_code(http::status_code code) { m_status_code = code; }

    unsigned long error_code() const { return m_error_code; } 

    void set_error_code(unsigned long code) { m_error_code = code; }

    const reason_phrase & reason_phrase() const { return m_reason_phrase; }

    void set_reason_phrase(const http::reason_phrase &reason) { m_reason_phrase = reason; }

    _ASYNCRTIMP std::string to_string() const;

    _ASYNCRTIMP std::wstring to_wstring() const;

    _http_server_context * _get_server_context() const { return m_server_context.get(); }

    void _set_server_context(std::unique_ptr<details::_http_server_context> server_context) { m_server_context = std::move(server_context); }

private:
    std::unique_ptr<_http_server_context> m_server_context;

    unsigned long m_error_code;

    http::status_code m_status_code;
    http::reason_phrase m_reason_phrase;
};


} // namespace details


#pragma region HTTP response message

/// <summary>
/// Public representation of a HTTP response.
/// </summary>
class http_response
{
public:

    /// <summary>
    /// Constructs a response with an empty status code, no headers, and no body.
    /// </summary>
    /// <returns>A new http response.</returns>
    http_response() : _m_impl(std::make_shared<details::_http_response>()) { }

    /// <summary>
    /// Constructs a response with given status code, no headers, and no body.
    /// </summary>
    /// <param name="status_code">HTTP status code to use in response.</param>
    /// <returns>A new http response.</returns>
    http_response(status_code code) 
        : _m_impl(std::make_shared<details::_http_response>(code)) { }

    /// <summary>
    /// Copy constructor.
    /// </summary>
    http_response(const http_response &response) : _m_impl(response._m_impl) {}

    /// <summary>
    /// Assignment operator.
    /// </summary>
    http_response &operator=(const http_response &response)
    {
        _m_impl = response._m_impl;
        return *this;
    }

    /// <summary>
    /// Get the status code of the response message.
    /// </summary>
    /// <returns>status code.</returns>
    status_code status_code() const { return _m_impl->status_code(); } 

    /// <summary>
    /// Sets the status code of the response message.
    /// </summary>
    /// <param name="status_code">Status code to set.</param>
    /// <remarks>
    /// This will clobber any previously set status code.
    /// </remarks>
    void set_status_code(http::status_code code) { _m_impl->set_status_code(code); }

    /// <summary>
    /// Gets the reason phrase of the response message.
    /// If no reason phrase is set it will default to the standard one corresponding to the status code.
    /// </summary>
    /// <returns>reason phrase.</returns>
    const reason_phrase & reason_phrase() const { return _m_impl->reason_phrase(); }

    /// <summary>
    /// Sets the reason phrase of the response message. 
    /// If no reason phrase is set it will default to the standard one corresponding to the status code.
    /// </summary>
    /// <param name="reason">The reason phrase to set.</param>
    void set_reason_phrase(const http::reason_phrase &reason) { _m_impl->set_reason_phrase(reason); }

    /// <summary>
    /// Get the headers of the response message.
    /// </summary>
    /// <returns>HTTP headers for this response.</returns>
    /// <remarks>
    /// Use the http_headers::add to fill in desired headers.
    /// </remarks>
    http_headers &headers() { return _m_impl->headers(); }

    /// <summary>
    /// Set the body of the message to a textual string and set the "Content-Type" header. Assumes
    /// the character encoding of the string is the OS's default code page and will perform appropriate
    /// conversions to UTF-8.
    /// </summary>
    /// <param name="body_text">String containing body text.</param>
    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".
    /// <remarks>
    /// This will clobber any previously set body data and "Content-Type" header.
    /// </remarks>
    void set_text(const std::string &body_text, const std::wstring &content_type = std::wstring(L"text/plain")) { _m_impl->set_text(body_text, content_type); }

    /// <summary>
    /// Sets the body of the message to contain a string. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'text/plain; charset=utf-8'.
    /// </summary>
    /// <param name="body_text">String containing body text.</param>
    /// <remarks>
    /// This will clobber any previously set body data.
    /// </remarks>
    void set_body(std::string &&body_text) { _m_impl->set_body(std::move(body_text)); }

    /// <summary>
    /// Sets the body of the message to contain a string. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'text/plain; charset=utf-16'.
    /// </summary>
    /// <param name="body_text">String containing body text.</param>
    /// <remarks>
    /// This will clobber any previously set body data.
    /// </remarks>
    void set_body(std::wstring &&body_text) { _m_impl->set_body(std::move(body_text)); }
    
    /// <summary>
    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'application/octet-stream'.
    /// </summary>
    /// <param name="body_data">Vector containing body data.</param>
    /// <remarks>
    /// This will clobber any previously set body data.
    /// </remarks>
    void set_body(std::vector<unsigned char> &&body_data) { _m_impl->set_body(std::move(body_data)); }

    /// <summary>
    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'application/octet-stream'.
    /// </summary>
    /// <param name="body_data">Address where body data is contiguously laid out in memory.</param>
    /// <param name="body_length">Number of bytes in body data.</param>
    /// <remarks>
    /// NOTE: with this method the body data must be avaliable AT LEAST until after the
    /// message has been sent.
    /// </remarks>
    void set_body(const unsigned char* body_data, const size_t body_length) { _m_impl->set_body(body_data, body_length); }

    /// <summary>
    /// Copies the contents of a memory block to this message. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'application/octet-stream'.
    /// </summary>
    /// <param name="body_data">Address where body data is contiguously laid out in memory.</param>
    /// <param name="body_length">Number of bytes in body data.</param>
    /// <remarks>
    /// This will clobber any previously set body data. 
    /// </remarks>
    void copy_body(const unsigned char * body_data, const size_t body_length) { _m_impl->copy_body(body_data, body_length); }

    /// <summary>
    /// Generate a string representation of the message, including the body when possible.
    /// </summary>
    /// <returns>A string representation of this HTTP request.</returns>
    std::string to_string() const { return _m_impl->to_string(); }

    /// <summary>
    /// Generate a string representation of the message, including the body when possible.
    /// </summary>
    /// <returns>A string representation of this HTTP request.</returns>
    std::wstring to_wstring() const { return _m_impl->to_wstring(); }

    /// <summary>
    /// Get a reference to the body of the response message.
    /// </summary>
    /// <returns>Pointer to raw message bytes.</returns>
    const unsigned char *body() const { return _m_impl->body(); }

    /// <summary>
    /// Gets the size of the body of the response message.
    /// </summary>
    /// <returns>Size in bytes.</returns>
    size_t body_size() const { return _m_impl->body_size(); }

    /// <summary>
    /// Extract the body of the response message as a string value, checking that the content type is a MIME text type.
    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
    /// </summary>
    /// <returns>String containing body of the message.</returns>
    std::string extract_string() { return _m_impl->extract_string(); }

    /// <summary>
    /// Extract the body of the response message as a wstring value, checking that the content type is a MIME text type.
    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
    /// </summary>
    /// <returns>String containing body of the message.</returns>
    std::wstring extract_wstring() { return _m_impl->extract_wstring(); }

    /// <summary>
    /// Extract the body of the response message into a json value, checking that the content type is application\json.
    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
    /// </summary>
    /// <returns>JSON value from the body of this message.</returns>
    http::json::value extract_json() { return _m_impl->extract_json(); }

    /// <summary>
    /// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on 
    /// </summary>
    /// <returns>The body of the message as a vector of bytes.</returns>
    std::vector<unsigned char> extract_vector() { return _m_impl->extract_vector(); }

    /// <summary>
    /// Get the error code of the response. This is used for errors other than HTTP status codes.
    /// </summary>
    /// <returns>The error code.</returns>
    unsigned long error_code() const { return _m_impl->error_code(); } 

    /// <summary>
    /// Set the error code of the response. This is used for errors other than HTTP status codes.
    /// </summary>
    /// <param name="code">The error code</param>
    void set_error_code(unsigned long code) { _m_impl->set_error_code(code); }

    /// <summary>
    /// Change the size of the allocated body.
    /// </summary>
    /// <param name="code">The error code</param>
    void resize_body(size_t size) { _m_impl->resize_body(size); }

    /// <summary>
    /// For internal use only
    /// </summary>
    details::data_buffer & _body() { return _m_impl->_body(); }

    void _get_body(unsigned char *& body_data, size_t & body_length) { _m_impl->_get_body(body_data, body_length); }

    http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); }
    void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context) { _m_impl->_set_server_context(std::move(server_context)); }

private:
    std::shared_ptr<http::details::_http_response> _m_impl;
};

#pragma endregion

namespace details {
/// <summary>
/// Internal representation of an HTTP request message.
/// </summary>
class _http_request : public http::details::_http_modifiable_msg_base, public std::tr1::enable_shared_from_this<_http_request>
{
public:

    _http_request()
        : m_initiated_response(0), m_server_context(nullptr), m_listener_path(L"")
    {}

    _http_request(const method &mtd)
        :  m_method(mtd), m_initiated_response(0), m_server_context(nullptr), m_listener_path(L"")
    {
        if(mtd.empty())
        {
            throw std::invalid_argument("Error: invalid HTTP method specified. Method can't be an empty string.");
        }
    }

    _http_request(std::unique_ptr<http::details::_http_server_context> server_context)
        : m_initiated_response(0), m_server_context(std::move(server_context)), m_listener_path(L"")
    {}

    virtual ~_http_request() { }

    http::method &method() { return m_method; }

    uri &request_uri() { return m_uri; }

    _ASYNCRTIMP uri relative_uri() const;

    _ASYNCRTIMP void set_request_uri(const uri&);

    _ASYNCRTIMP std::string to_string() const;

    _ASYNCRTIMP std::wstring to_wstring() const;

    _ASYNCRTIMP pplx::task<void> reply(http_response response);

    pplx::task<http_response> get_response()
    {
        return pplx::task<http_response>(m_response);
    }

    pplx::task<void> reply(status_code status)
    {
        return reply(http_response(status));
    }

    _ASYNCRTIMP pplx::task<void> reply(status_code status, const http::json::value &body_data);

    _ASYNCRTIMP pplx::task<void> reply(status_code status, const std::string &text);

    _ASYNCRTIMP pplx::task<void> reply(status_code status, std::string &&text);

    _ASYNCRTIMP pplx::task<void> reply(status_code status, const std::wstring &content_type, const std::string &body_data);

    _ASYNCRTIMP pplx::task<void> reply(status_code status, const std::wstring &content_type, std::string &&body_data);

    _ASYNCRTIMP pplx::task<void> _reply_if_not_already(status_code status);

    http::details::_http_server_context * _get_server_context() const { return m_server_context.get(); }

    void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context) { m_server_context = std::move(server_context); }

    void _set_listener_path(const uri::encoded_string &path) { m_listener_path = path; }

private:

    // Actual initiates sending the response, without checking if a response has already been sent.
    pplx::task<void> _reply_impl(http_response response);

    // Tracks whether or not a response as already been started for this message.
    volatile long m_initiated_response;

    http::method m_method;

    http::uri m_uri;
    uri::encoded_string m_listener_path;
    std::unique_ptr<http::details::_http_server_context> m_server_context;

    pplx::task_completion_event<http_response> m_response;
};


}  // namespace details

#pragma region HTTP Request

/// <summary>
/// Public representation of a HTTP request.
/// </summary>
class http_request
{
public:
    /// <summary>
    /// Constructs a new HTTP request with the given request method.
    /// </summary>
    /// <param name="method">Request method.<param>
    http_request() 
        : _m_impl(std::make_shared<http::details::_http_request>()) {}

    /// <summary>
    /// Constructs a new HTTP request with the given request method.
    /// </summary>
    /// <param name="method">Request method.<param>
    http_request(const method &method) 
        : _m_impl(std::make_shared<http::details::_http_request>(method)) {}

    /// <summary>
    /// Copy constructor.
    /// </summary>
    http_request(const http_request &message) : _m_impl(message._m_impl) {}

    /// <summary>
    /// Destructor frees any held resources.
    /// </summary>
    ~http_request() {}

    /// <summary>
    /// Assignment operator.
    /// </summary>
    http_request &operator=(const http_request &message) 
    {
        _m_impl = message._m_impl;
        return *this;
    }

    /// <summary>
    /// Get the method (GET/PUT/POST/DELETE) of the request message.
    /// </summary>
    /// <returns>Request method of this HTTP request.</returns>
    const method &method() const { return _m_impl->method(); }

    /// <summary>
    /// Get the method (GET/PUT/POST/DELETE) of the request message.
    /// </summary>
    /// <returns>Request method of this HTTP request.</returns>
    void set_method(const ::http::method& method) const { _m_impl->method() = method; }

    /// <summary>
    /// Get the underling URI of the request message.
    /// </summary>
    /// <returns>The uri of this message.</returns>
    uri request_uri() const { return _m_impl->request_uri(); }

    /// <summary>
    /// Set the underling URI of the request message.
    /// </summary>
    /// <param name="uri">The uri for this message.</param>
    void set_request_uri(const uri& uri) { return _m_impl->set_request_uri(uri.to_encoded_string()); }

    /// <summary>
    /// Sets the URI path, query, and fragment portion of this request message, first encoding it.
    /// This will be appended to the base URI specified at construction of the http_client.
    /// </summary>
    /// <param name="path_query_fragment">Path, fragment, and query as an unencoded string.</param>
    /// <remarks>
    /// This will clobber any previously set value.
    /// </remarks>
    void set_request_uri(const std::wstring &path_query_fragment) 
    { 
        // Make sure starts with a leading slash, if not add one for the user.
        if(!path_query_fragment.empty() && path_query_fragment[0] != '/')
        {
            _m_impl->set_request_uri(uri(uri::encode_uri(L"/" + path_query_fragment)));
        }
        else
        {
            _m_impl->set_request_uri(uri(uri::encode_uri(path_query_fragment)));
        }
    }

    /// <summary>
    /// Sets the URI path, query, and fragment portion of this request message, first encoding it.
    /// This will be appended to the base URI specified at construction of the http_client.
    /// </summary>
    /// <param name="path_query_fragment">Path, fragment, and query as an unencoded string.</param>
    /// <remarks>
    /// This will clobber any previously set value.
    /// </remarks>
    void set_request_uri(const wchar_t* path_query_fragment)
    {
        set_request_uri(std::wstring(path_query_fragment));
    }

    /// <summary>
    /// Sets the URI path, query, and fragment portion of this request message.
    /// This will be appended to the base URI specified at construction of the http_client.
    /// </summary>
    /// <param name="path_query_fragment">Path, fragment, and query as an encoded string.</param>
    /// <remarks>
    /// This will clobber any previously set value.
    /// </remarks>
    void set_request_uri(const uri::encoded_string &path_query_fragment) 
    { 
        auto str = path_query_fragment.to_wstring();

        if(!str.empty() && str[0] != '/')
        {
            _m_impl->set_request_uri(uri(uri::encoded_string(L"/" + str))); 
        }
        else
        {
            _m_impl->set_request_uri(uri(path_query_fragment)); 
        }
    }

    /// <summary>
    /// Gets a reference the URI path, query, and fragment part of this request message.
    /// This will be appended to the base URI specified at construction of the http_client.
    /// </summary>
    /// <returns>A string.</returns>
    /// <remarks>When the request is the one passed to a listener's handler, the
    /// relative URI is the request URI less the listener's path. In all other circumstances,
    /// request_uri() and relative_uri() will return the same value.
    /// </remarks>
    uri relative_uri() const { return _m_impl->relative_uri(); }

    /// <summary>
    /// Get a reference to the headers of the response message.
    /// </summary>
    /// <returns>HTTP headers for this response.</returns>
    /// <remarks>
    /// Use the http_headers::add to fill in desired headers.
    /// </remarks>
    http_headers &headers() { return _m_impl->headers(); }

    /// <summary>
    /// Get the body of the request message.
    /// </summary>
    /// <returns>Pointer to raw bytes of request message body.</returns>
    const unsigned char * body() const { return _m_impl->_body().body(); }

    /// <summary>
    /// Gets the size of the body of the request message.
    /// </summary>
    /// <returns>Size in bytes.</returns>
    size_t body_size() const { return _m_impl->body_size(); }

    /// <summary>
    /// Extract the body of the request message as a string value, checking that the content type is a MIME text type.
    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
    /// </summary>
    /// <returns>String containing body of the message.</returns>
    std::string extract_string() { return _m_impl->extract_string(); }

    /// <summary>
    /// Extract the body of the request message as a wstring value, checking that the content type is a MIME text type.
    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
    /// </summary>
    /// <returns>String containing body of the message.</returns>
    std::wstring extract_wstring() { return _m_impl->extract_wstring(); }

    /// <summary>
    /// Extract the body of the request message into a json value, checking that the content type is application\json.
    /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
    /// </summary>
    /// <returns>JSON value from the body of this message.</returns>
    http::json::value extract_json() { return _m_impl->extract_json(); }

    /// <summary>
    /// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on 
    /// </summary>
    /// <returns>The body of the message as a vector of bytes.</returns>
    std::vector<unsigned char> extract_vector() { return _m_impl->extract_vector(); }

    /// <summary>
    /// Set the body of the message to a textual string and set the "Content-Type" header. Assumes
    /// the character encoding of the string is the OS's default code page and will perform appropriate
    /// conversions to UTF-8.
    /// </summary>
    /// <param name="body_text">String containing body text.</param>
    /// <param name="content_type">MIME type to set the "Content-Type" header to. Default to "text/plain".
    /// <remarks>
    /// This will clobber any previously set body data and "Content-Type" header.
    /// </remarks>
    void set_text(const std::string &body_text, const std::wstring &content_type = std::wstring(L"text/plain")) { _m_impl->set_text(body_text, content_type); }

    /// <summary>
    /// Sets the body of the message to contain a string. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'text/plain; charset=utf-8'.
    /// </summary>
    /// <param name="body_text">String containing body text.</param>
    /// <remarks>
    /// This will clobber any previously set body data.
    /// </remarks>
    void set_body(std::string &&body_text) { _m_impl->set_body(std::move(body_text)); }

    /// <summary>
    /// Sets the body of the message to contain a string. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'text/plain; charset=utf-16'.
    /// </summary>
    /// <param name="body_text">String containing body text.</param>
    /// <remarks>
    /// This will clobber any previously set body data.
    /// </remarks>
    void set_body(std::wstring &&body_text) { _m_impl->set_body(std::move(body_text)); }
    
    /// <summary>
    /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'application/octet-stream'.
    /// </summary>
    /// <param name="body_data">Vector containing body data.</param>
    /// <remarks>
    /// This will clobber any previously set body data.
    /// </remarks>
    void set_body(std::vector<unsigned char> &&body_data) { _m_impl->set_body(std::move(body_data)); }
    
    /// <summary>
    /// Sets the body of the message to the contents of a memory block. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'application/octet-stream'.
    /// </summary>
    /// <param name="body_data">Address where body data is contiguously laid out in memory.</param>
    /// <param name="body_length">Number of bytes in body data.</param>
    /// <remarks>
    /// NOTE: with this method the body data must be avaliable AT LEAST until after the
    /// message has been sent.
    ///
    /// This will clobber any previously set body data. 
    /// </remarks>
    void set_body(const unsigned char * body_data, const size_t body_length) { _m_impl->set_body(body_data, body_length); }

    /// <summary>
    /// Copies the contents of a memory block to this message. If the 'Content-Type'
    /// header hasn't already been set it will be set to 'application/octet-stream'.
    /// </summary>
    /// <param name="body_data">Address where body data is contiguously laid out in memory.</param>
    /// <param name="body_length">Number of bytes in body data.</param>
    /// <remarks>
    /// This will clobber any previously set body data. 
    /// </remarks>
    void copy_body(const unsigned char * body_data, const size_t body_length) { _m_impl->copy_body(body_data, body_length); }

    /// <summary>
    /// Change the size of the allocated body.
    /// </summary>
    /// <param name="code">The error code</param>
    void resize_body(size_t size) { _m_impl->resize_body(size); }

    /// <summary>
    /// Asynchronously responses to this HTTP request.
    /// </summary>
    /// <param name="response">Response to send.</param>
    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
    pplx::task<void> reply(http_response response) { return _m_impl->reply(response); }

    /// <summary>
    /// Asynchronously responses to this HTTP request.
    /// </summary>
    /// <param name="status">Response status code.</param>
    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
    pplx::task<void> reply(status_code status) { return _m_impl->reply(status); }

    /// <summary>
    /// Responses to this HTTP request.
    /// </summary>
    /// <param name="status">Response status code.</param>
    /// <param name="body_data">Json value to use in the response body.</param>
    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
    pplx::task<void> reply(const status_code status, const http::json::value &body_data) { return _m_impl->reply(status, body_data); }

    /// <summary>
    /// Responses to this HTTP request. Assumes the string text is OS's default code page and
    /// converts to utf-8.
    /// </summary>
    /// <param name="status">Response status code.</param>
    /// <param name="text">String containing text to use in the response body.</param>
    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
    pplx::task<void> reply(status_code status, const std::string &text) { return _m_impl->reply(status, text); }

    /// <summary>
    /// Responses to this HTTP request.
    /// </summary>
    /// <param name="status">Response status code.</param>
    /// <param name="content_type">Content type of the body.</param>
    /// <param name="body_data">String containing the text to use in the response body.</param>
    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
    /// <remarks>
    /// The response may be sent either synchronously or asychronously depending on an internal
    /// algorithm on whether we decide or not to copy the body data. Either way callers of this function do NOT
    /// need to block waiting for the response to be sent to before the body data is destroyed or goes
    /// out of scope.
    /// </remarks>
    pplx::task<void> reply(status_code status, const std::wstring &content_type, const std::string &body_data) 
    { 
        return _m_impl->reply(status, content_type, body_data); 
    }

    /// <summary>
    /// Responses to this HTTP request.
    /// </summary>
    /// <param name="status">Response status code.</param>
    /// <param name="content_type">Content type of the body.</param>
    /// <param name="body_data">String containing the text to use in the response body.</param>
    /// <returns>An asynchronous operation that is completed once response is sent.</returns>
    pplx::task<void> reply(status_code status, const std::wstring &content_type, std::string &&body_data) 
    { 
        return _m_impl->reply(status, content_type, std::move(body_data));
    }

    /// <summary>
    /// Get a task representing the response that will eventually be sent.
    /// </summary>
    /// <returns>A task that is completed once response is sent.</returns>
    pplx::task<http_response> get_response()
    {
        return _m_impl->get_response();
    }

    /// <summary>
    /// Generate a string representation of the message, including the body when possible.
    /// </summary>
    /// <returns>A string representation of this HTTP request.</returns>
    std::string to_string() const { return _m_impl->to_string(); }

    /// <summary>
    /// Generate a string representation of the message, including the body when possible.
    /// </summary>
    /// <returns>A string representation of this HTTP request.</returns>
    std::wstring to_wstring() const { return _m_impl->to_wstring(); }

    /// <summary>
    /// Gets the body of the message.
    /// </summary>
    /// <param name="body_data">The body data.</param>
    /// <param name="body_length">The number of bytes.</param>
    void _get_body(unsigned char *& body_data, size_t & body_length) { _m_impl->_get_body(body_data, body_length); }

    /// <summary>
    /// Send a response if one has not already been sent.
    /// </summary>
    pplx::task<void> _reply_if_not_already(status_code status) { return _m_impl->_reply_if_not_already(status); }

    /// <summary>
    /// Gets the server context associated with this HTTP message.
    /// </summary>
    http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); }

    /// <summary>
    /// These are used for the initial creation of the http request.
    /// </summary>
    static http_request _create_request(std::unique_ptr<http::details::_http_server_context> server_context) { return http_request(std::move(server_context)); }
    void _set_server_context(std::unique_ptr<http::details::_http_server_context> server_context) { _m_impl->_set_server_context(std::move(server_context)); }

    void _set_listener_path(const uri::encoded_string &path) { _m_impl->_set_listener_path(path); }

    /// <summary>
    /// For internal use only
    /// </summary>
    details::data_buffer & _body() { return _m_impl->_body(); }

private:
    friend class http::details::_http_request;

    http_request(std::unique_ptr<http::details::_http_server_context> server_context) : _m_impl(std::shared_ptr<details::_http_request>(new details::_http_request(std::move(server_context)))) {}
    http_request(std::shared_ptr<http::details::_http_request> message) : _m_impl(message) {}

    // This constructor overload is only needed because when 'const' functions in _http_request need to log messages (log::post)
    // call shared_from_this() then get back a std::shared_ptr<const details::_http_request>. 
    // From which we need to create a http_request.
    http_request(std::shared_ptr<const http::details::_http_request> message)
    {
        _m_impl = std::shared_ptr<http::details::_http_request>(const_cast<http::details::_http_request *>(message.get()));
    }

    std::shared_ptr<http::details::_http_request> _m_impl;
};

#pragma endregion

/// <summary>
/// HTTP client handler class, used to represent a HTTP pipeline stage.
/// </summary>
/// <remarks>
/// When a request goes out, it passed through a series of stages, customizable by
/// the application and/or libraries. The default stage will interact with lower-level
/// communication layers to actually send the message on a network. When creating a client
/// instance, an application may add pipeline stages in front of the already existing
/// stages. Each stage has a reference to the next stage available in the 'next_stage()'
/// value.
/// </remarks>
class http_pipeline_stage
{
public:
    virtual pplx::task<http_response> propagate(http_request request) = 0;

protected:
    std::shared_ptr<http_pipeline_stage> get_next_stage() const
    {
        return m_next_stage;
    }
private:
    friend class http_pipeline;

    void set_next_stage(std::shared_ptr<http_pipeline_stage> next)
    {
        m_next_stage = next; 
    }

    std::shared_ptr<http_pipeline_stage> m_next_stage;
};

class http_pipeline
{
protected:
    http_pipeline() 
    {
    }

    http_pipeline(std::shared_ptr<http_pipeline_stage> last) : m_last_stage(last) 
    {
    }

    pplx::task<http_response> propagate(http_request request)
    {
        std::shared_ptr<http_pipeline_stage> first;
        {
            actors::details::win32::reentrant_lock::scoped_lock l(m_lock);
            first = (m_stages.size() > 0) ? m_stages[0] : m_last_stage;
        }
        return first->propagate(request);
    }

    /// <summary>
    /// Add an HTTP pipeline stage to the pipeline.
    /// </summary>
    /// <param name="stage">A pipeline stage.</param>
    void append(std::shared_ptr<http_pipeline_stage> stage)
    {
        actors::details::win32::reentrant_lock::scoped_lock l(m_lock);

        if ( m_stages.size() > 0 )
        {
            std::shared_ptr<http_pipeline_stage> penultimate = m_stages[m_stages.size()-1];
            penultimate->set_next_stage(stage);
        }
        stage->set_next_stage(m_last_stage);

        m_stages.push_back(stage);
    }

    void set_last_stage(std::shared_ptr<http_pipeline_stage> last)
    {
        m_last_stage = last;
    }

    http_pipeline(const http_pipeline&& other) :
        m_stages(std::move(other.m_stages)),
        m_last_stage(std::move(other.m_last_stage))
    {
    }

    http_pipeline &http_pipeline::operator=(const http_pipeline &&other)
    {
        if(this != &other)
        {
            m_stages = std::move(other.m_stages);
            m_last_stage = std::move(other.m_last_stage);
        }
        return *this;
    }


private:
    // No copy or assignment.
    http_pipeline & operator=(const http_pipeline &);
    http_pipeline(const http_pipeline &);

    // The vector of pipeline stages.
    std::vector<std::shared_ptr<http_pipeline_stage>> m_stages;

    // The last stage is always set up by the client or listener and cannot
    // be changed. All application-defined stages are executed before the
    // last stage, which is typically a send or dispatch.
    std::shared_ptr<http_pipeline_stage> m_last_stage;

    actors::details::win32::reentrant_lock m_lock;
};

} // namespace http
